package ru.amse.stroganova.io;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import ru.amse.stroganova.graph.AbstractGraph;
import ru.amse.stroganova.graph.Edge;
import ru.amse.stroganova.graph.Vertex;
import ru.amse.stroganova.presentation.GraphPresentation;
import ru.amse.stroganova.presentation.VertexPresentation;

/**
 * Implements saving graphs and presentations to xml file.
 * 
 * @author Dasha Stroganova
 *
 */
public class GraphSaver {

	private final File file;
	
	private Document document;
	
	private Element graphElement;
	
	private final Map<Vertex, String> verticesIDs;
	
	/**
	 * Creates new GraphSaver for given file.
	 * 
	 * @param file
	 */
	public GraphSaver(File file) {
		this.file= file;
		verticesIDs = new HashMap<Vertex, String>();
	}
	
	/**
	 * Saves given graph to xml file.
	 * 
	 * @param graph
	 * @throws ParserConfigurationException 
	 * @throws TransformerException 
	 */
	public void saveGraph(AbstractGraph graph) throws ParserConfigurationException, TransformerException {
		createDOMTreeRoot();
		graphElement.appendChild(createModelElement(graph));
		writeTreeToFile();
	}
	
	/**
	 * Saves given graph presentation to xml file.
	 * 
	 * @param graphPresentation
	 * @throws ParserConfigurationException 
	 * @throws ParserConfigurationException 
	 * @throws TransformerException 
	 * @throws TransformerException 
	 */
	public void saveGraphPresentation(GraphPresentation graphPresentation) throws ParserConfigurationException, TransformerException {
		createDOMTreeRoot();
		graphElement.appendChild(createModelElement(graphPresentation.getGraph()));
		graphElement.appendChild(createPresentationElement(graphPresentation));
		writeTreeToFile();
	}
	
	private Element createModelElement(AbstractGraph graph) {
		Element model = document.createElement("model");
		model.setAttribute("directed", (graph.isDirected() ? "yes" : "no"));

		List<Vertex> vertices = new ArrayList<Vertex>(graph.getVertices());
		Set<Edge> edges = new HashSet<Edge>();
		for (int i = 0; i < vertices.size(); i++) {
			for (Edge edge : vertices.get(i).getOutgoingEdges()) {
				edges.add(edge);
			}
			verticesIDs.put(vertices.get(i), "v" + i);
			Element vertexElement = document.createElement("vertex");
			 vertexElement.setAttribute("id", "v" + i);
			model.appendChild(vertexElement);
		}
		
		for (Edge edge : edges) {
			Element edgeElement = document.createElement("edge");
			edgeElement.setAttribute("sourceID", verticesIDs.get(edge.getSource()));
			edgeElement.setAttribute("destinationID", verticesIDs.get(edge.getDestination()));
			edgeElement.setAttribute("weight", String.valueOf(edge.getWeight()));
			model.appendChild(edgeElement);
		}
		
		return model;
	}
	
	private Element createPresentationElement(GraphPresentation graphPresentation) {
		Element presentation = document.createElement("presentation");
		presentation.setAttribute("weighted", (graphPresentation.isWeighted() ? "yes" : "no"));
		
		for (VertexPresentation vp : graphPresentation.getVertexPresentations()) {
			Element centerElement = document.createElement("center");
			centerElement.setAttribute("vertexID", verticesIDs.get(vp.getVertex()));
			centerElement.setAttribute("x", String.valueOf(vp.getCenter().x));
			centerElement.setAttribute("y", String.valueOf(vp.getCenter().y));
			presentation.appendChild(centerElement);
		}
		
		return presentation;
	}
	
	private void createDOMTreeRoot() throws ParserConfigurationException {
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = factory.newDocumentBuilder();
		DOMImplementation domImpl = builder.getDOMImplementation();
		document = domImpl.createDocument(null, null, null);
		graphElement = document.createElement("graph");
		document.appendChild(graphElement);
	}
	
	private void writeTreeToFile() throws TransformerException { 
		TransformerFactory transformerFactory = TransformerFactory.newInstance();
		Transformer transformer = transformerFactory.newTransformer();
		transformer.setOutputProperty("indent", "yes");
		transformer.transform(new DOMSource(document), new StreamResult(file));
	}
}
